/*
===========================================================================

Doom 3 BFG Edition GPL Source Code
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.

This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").

Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Doom 3 BFG Edition Source Code.  If not, see <http://www.gnu.org/licenses/>.

In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code.  If not, please request a copy in writing from id Software at the address below.

If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.

===========================================================================
*/

#include "Precompiled.h"
#include "globaldata.h"

#include "i_system.h"
#include "z_zone.h"

#include "m_swap.h"

#include "w_wad.h"

#include "doomdef.h"
#include "r_local.h"
#include "p_local.h"

#include "doomstat.h"
#include "r_sky.h"

#ifdef LINUX
	#include  <alloca.h>
#endif


#include "r_data.h"

#include <vector>

//
// Graphics.
// DOOM graphics for walls and ::g->sprites
// is stored in vertical runs of opaque pixels (posts).
// A column is composed of zero or more posts,
// a patch or sprite is composed of zero or more columns.
//



//
// Texture definition.
// Each texture is composed of one or more patches,
// with patches being lumps stored in the WAD.
// The lumps are referenced by number, and patched
// into the rectangular texture space using origin
// and possibly other attributes.
//


//
// Texture definition.
// A DOOM wall texture is a list of patches
// which are to be combined in a predefined order.
//


// A single patch from a texture definition,
//  basically a rectangular area within
//  the texture rectangle.


// A maptexturedef_t describes a rectangular texture,
//  which is composed of one or more mappatch_t structures
//  that arrange graphic patches.






// for global animation

// needed for pre rendering



//
// MAPTEXTURE_T CACHING
// When a texture is first needed,
//  it counts the number of composite columns
//  required in the texture and allocates space
//  for a column directory and any new columns.
// The directory will simply point inside other patches
//  if there is only one patch in a given column,
//  but any columns with multiple patches
//  will have new postColumn_ts generated.
//



//
// R_DrawColumnInCache
// Clip and draw a column
//  from a patch into a cached post.
//
void
R_DrawColumnInCache
( postColumn_t*	patch,
  byte*			cache,
  int			originy,
  int			cacheheight )
{
	int		count;
	int		position;
	byte*	source;
	byte*	dest;

	dest = ( byte* )cache + 3;

	while( patch->topdelta != 0xff )
	{
		source = ( byte* )patch + 3;
		count = patch->length;
		position = originy + patch->topdelta;

		if( position < 0 )
		{
			count += position;
			position = 0;
		}

		if( position + count > cacheheight )
		{
			count = cacheheight - position;
		}

		if( count > 0 )
		{
			memcpy( cache + position, source, count );
		}

		patch = ( postColumn_t* )( ( byte* )patch + patch->length + 4 );
	}
}



//
// R_GenerateComposite
// Using the texture definition,
//  the composite texture is created from the patches,
//  and each column is cached.
//
void R_GenerateComposite( int texnum )
{
	byte*			block;
	texture_t*		texture;
	texpatch_t*		patch;
	patch_t*		realpatch;
	int				x;
	int				x1;
	int				x2;
	int				i;
	postColumn_t*	patchcol;
	short*			collump;
	unsigned short*	colofs;

	texture = ::g->s_textures[texnum];

	block = ( byte* )DoomLib::Z_Malloc( ::g->s_texturecompositesize[texnum],
										PU_CACHE_SHARED,
										&::g->s_texturecomposite[texnum] );

	collump = ::g->s_texturecolumnlump[texnum];
	colofs = ::g->s_texturecolumnofs[texnum];

	// Composite the columns together.
	patch = texture->patches;

	for( i = 0 , patch = texture->patches;
			i < texture->patchcount;
			i++, patch++ )
	{
		realpatch = ( patch_t* )W_CacheLumpNum( patch->patch, PU_CACHE_SHARED );
		x1 = patch->originx;
		x2 = x1 + SHORT( realpatch->width );

		if( x1 < 0 )
		{
			x = 0;
		}
		else
		{
			x = x1;
		}

		if( x2 > texture->width )
		{
			x2 = texture->width;
		}

		for( ; x < x2 ; x++ )
		{
			// Column does not have multiple patches?
			if( collump[x] >= 0 )
			{
				continue;
			}

			patchcol = ( postColumn_t* )( ( byte* )realpatch
										  + LONG( realpatch->columnofs[x - x1] ) );
			R_DrawColumnInCache( patchcol,
								 block + colofs[x],
								 patch->originy,
								 texture->height );
		}

	}
}



//
// R_GenerateLookup
//
void R_GenerateLookup( int texnum )
{
	texture_t*		texture;
	texpatch_t*		patch;
	patch_t*		realpatch;
	int			x;
	int			x1;
	int			x2;
	int			i;
	short*		collump;
	unsigned short*	colofs;

	texture = ::g->s_textures[texnum];

	// Composited texture not created yet.
	::g->s_texturecomposite[texnum] = 0;

	::g->s_texturecompositesize[texnum] = 0;
	collump = ::g->s_texturecolumnlump[texnum];
	colofs = ::g->s_texturecolumnofs[texnum];

	// Now count the number of columns
	//  that are covered by more than one patch.
	// Fill in the lump / offset, so columns
	//  with only a single patch are all done.
	std::vector<byte> patchcount( texture->width, 0 );
	patch = texture->patches;

	for( i = 0 , patch = texture->patches;
			i < texture->patchcount;
			i++, patch++ )
	{
		realpatch = ( patch_t* )W_CacheLumpNum( patch->patch, PU_CACHE_SHARED );
		x1 = patch->originx;
		x2 = x1 + SHORT( realpatch->width );

		if( x1 < 0 )
		{
			x = 0;
		}
		else
		{
			x = x1;
		}

		if( x2 > texture->width )
		{
			x2 = texture->width;
		}
		for( ; x < x2 ; x++ )
		{
			patchcount[x]++;
			collump[x] = patch->patch;
			colofs[x] = LONG( realpatch->columnofs[x - x1] ) + 3;
		}
	}

	for( x = 0 ; x < texture->width ; x++ )
	{
		if( !patchcount[x] )
		{
			I_Printf( "R_GenerateLookup: column without a patch (%s)\n",
					  texture->name );
			return;
		}
		// I_Error ("R_GenerateLookup: column without a patch");

		if( patchcount[x] > 1 )
		{
			// Use the cached block.
			collump[x] = -1;
			colofs[x] = ::g->s_texturecompositesize[texnum];

			if( ::g->s_texturecompositesize[texnum] > 0x10000 - texture->height )
			{
				I_Error( "R_GenerateLookup: texture %i is >64k",
						 texnum );
			}

			::g->s_texturecompositesize[texnum] += texture->height;
		}
	}
}




//
// R_GetColumn
//
byte*
R_GetColumn
( int		tex,
  int		col )
{
	int		lump;
	int		ofs;

	col &= ::g->s_texturewidthmask[tex];
	lump = ::g->s_texturecolumnlump[tex][col];
	ofs = ::g->s_texturecolumnofs[tex][col];

	if( lump > 0 )
	{
		return ( byte* )W_CacheLumpNum( lump, PU_CACHE_SHARED ) + ofs;
	}

	if( !::g->s_texturecomposite[tex] )
	{
		R_GenerateComposite( tex );
	}

	return ::g->s_texturecomposite[tex] + ofs;
}




//
// R_InitTextures
// Initializes the texture list
//  with the s_textures from the world map.
//
void R_InitTextures( void )
{
	maptexture_t*	mtexture;
	texture_t*		texture;
	mappatch_t*		mpatch;
	texpatch_t*		patch;

	int			i;
	int			j;

	int*		maptex;
	int*		maptex2;
	int*		maptex1;

	char		name[9];
	char*		names;
	char*		name_p;

	int			totalwidth;
	int			nummappatches;
	int			offset;
	int			maxoff;
	int			maxoff2;
	int			numtextures1;
	int			numtextures2;

	int*		directory;

	int			temp1;
	int			temp2;
	int			temp3;


	// Load the patch names from pnames.lmp.
	name[8] = 0;
	names = ( char* )W_CacheLumpName( "PNAMES", PU_CACHE_SHARED );
	nummappatches = LONG( *( ( int* )names ) );
	name_p = names + 4;

	std::vector<int> patchlookup( nummappatches );

	for( i = 0 ; i < nummappatches ; i++ )
	{
		strncpy( name, name_p + i * 8, 8 );
		patchlookup[i] = W_CheckNumForName( name );
	}
	Z_Free( names );

	if( ::g->s_numtextures == 0 )
	{

		// Load the map texture definitions from textures.lmp.
		// The data is contained in one or two lumps,
		//  TEXTURE1 for shareware, plus TEXTURE2 for commercial.
		maptex = maptex1 = ( int* )W_CacheLumpName( "TEXTURE1", PU_CACHE_SHARED ); // ALAN:  LOADTIME
		numtextures1 = LONG( *maptex );
		maxoff = W_LumpLength( W_GetNumForName( "TEXTURE1" ) );
		directory = maptex + 1;

		if( W_CheckNumForName( "TEXTURE2" ) != -1 )
		{
			maptex2 = ( int* )W_CacheLumpName( "TEXTURE2", PU_CACHE_SHARED ); // ALAN:  LOADTIME
			numtextures2 = LONG( *maptex2 );
			maxoff2 = W_LumpLength( W_GetNumForName( "TEXTURE2" ) );
		}
		else
		{
			maptex2 = NULL;
			numtextures2 = 0;
			maxoff2 = 0;
		}


		::g->s_numtextures = numtextures1 + numtextures2;

		::g->s_textures = ( texture_t** )DoomLib::Z_Malloc( ::g->s_numtextures * sizeof( texture_t* ), PU_STATIC_SHARED, 0 );
		::g->s_texturecolumnlump = ( short** )DoomLib::Z_Malloc( ::g->s_numtextures * sizeof( short* ), PU_STATIC_SHARED, 0 );
		::g->s_texturecolumnofs = ( unsigned short** )DoomLib::Z_Malloc( ::g->s_numtextures * sizeof( unsigned short* ), PU_STATIC_SHARED, 0 );
		::g->s_texturewidthmask = ( int* )DoomLib::Z_Malloc( ::g->s_numtextures * 4, PU_STATIC_SHARED, 0 );
		::g->s_textureheight = ( fixed_t* )DoomLib::Z_Malloc( ::g->s_numtextures * 4, PU_STATIC_SHARED, 0 );
		::g->s_texturecomposite = ( byte** )DoomLib::Z_Malloc( ::g->s_numtextures * sizeof( byte* ), PU_STATIC_SHARED, 0 );
		::g->s_texturecompositesize = ( int* )DoomLib::Z_Malloc( ::g->s_numtextures * 4, PU_STATIC_SHARED, 0 );

		totalwidth = 0;

		//	Really complex printing shit...
		temp1 = W_GetNumForName( "S_START" ); // P_???????
		temp2 = W_GetNumForName( "S_END" ) - 1;
		temp3 = ( ( temp2 - temp1 + 63 ) / 64 ) + ( ( ::g->s_numtextures + 63 ) / 64 );
		I_Printf( "[" );
		for( i = 0; i < temp3; i++ )
		{
			I_Printf( " " );
		}
		I_Printf( "         ]" );
		for( i = 0; i < temp3; i++ )
		{
			I_Printf( "\x8" );
		}
		I_Printf( "\x8\x8\x8\x8\x8\x8\x8\x8\x8\x8" );

		for( i = 0 ; i < ::g->s_numtextures ; i++, directory++ )
		{
			if( !( i & 63 ) )
			{
				I_Printf( "." );
			}

			if( i == numtextures1 )
			{
				// Start looking in second texture file.
				maptex = maptex2;
				maxoff = maxoff2;
				directory = maptex + 1;
			}

			offset = LONG( *directory );

			if( offset > maxoff )
			{
				I_Error( "R_InitTextures: bad texture directory" );
			}

			mtexture = ( maptexture_t* )( ( byte* )maptex + offset );

			texture = ::g->s_textures[i] = ( texture_t* )DoomLib::Z_Malloc( sizeof( texture_t )
										   + sizeof( texpatch_t ) * ( SHORT( mtexture->patchcount ) - 1 ), PU_STATIC_SHARED, 0 );

			texture->width = SHORT( mtexture->width );
			texture->height = SHORT( mtexture->height );
			texture->patchcount = SHORT( mtexture->patchcount );

			memcpy( texture->name, mtexture->name, sizeof( texture->name ) );
			mpatch = &mtexture->patches[0];
			patch = &texture->patches[0];

			for( j = 0 ; j < texture->patchcount ; j++, mpatch++, patch++ )
			{
				patch->originx = SHORT( mpatch->originx );
				patch->originy = SHORT( mpatch->originy );
				patch->patch = patchlookup[SHORT( mpatch->patch )];
				if( patch->patch == -1 )
				{
					I_Error( "R_InitTextures: Missing patch in texture %s",
							 texture->name );
				}
			}
			::g->s_texturecolumnlump[i] = ( short* )DoomLib::Z_Malloc( texture->width * 2, PU_STATIC_SHARED, 0 );
			::g->s_texturecolumnofs[i] = ( unsigned short* )DoomLib::Z_Malloc( texture->width * 2, PU_STATIC_SHARED, 0 );

			j = 1;
			while( j * 2 <= texture->width )
			{
				j <<= 1;
			}

			::g->s_texturewidthmask[i] = j - 1;
			::g->s_textureheight[i] = texture->height << FRACBITS;

			totalwidth += texture->width;
		}

		Z_Free( maptex1 );
		if( maptex2 )
		{
			Z_Free( maptex2 );
		}


		// Precalculate whatever possible.
		for( i = 0 ; i < ::g->s_numtextures ; i++ )
		{
			R_GenerateLookup( i );
		}
	}

	// ALAN:  These animations are done globally -- can it be shared?
	// Create translation table for global animation.
	::g->texturetranslation = ( int* )DoomLib::Z_Malloc( ( ::g->s_numtextures + 1 ) * 4, PU_STATIC, 0 );

	for( i = 0 ; i < ::g->s_numtextures ; i++ )
	{
		::g->texturetranslation[i] = i;
	}
}



//
// R_InitFlats
//
void R_InitFlats( void )
{
	int		i;

	::g->firstflat = W_GetNumForName( "F_START" ) + 1;
	::g->lastflat = W_GetNumForName( "F_END" ) - 1;
	::g->numflats = ::g->lastflat - ::g->firstflat + 1;

	// Create translation table for global animation.
	::g->flattranslation = ( int* )DoomLib::Z_Malloc( ( ::g->numflats + 1 ) * 4, PU_STATIC, 0 );

	for( i = 0 ; i < ::g->numflats ; i++ )
	{
		::g->flattranslation[i] = i;
	}
}


//
// R_InitSpriteLumps
// Finds the width and hoffset of all ::g->sprites in the wad,
//  so the sprite does not need to be cached completely
//  just for having the header info ready during rendering.
//
void R_InitSpriteLumps( void )
{
	int		i;
	patch_t*	patch;

	::g->firstspritelump = W_GetNumForName( "S_START" ) + 1;
	::g->lastspritelump = W_GetNumForName( "S_END" ) - 1;

	::g->numspritelumps = ::g->lastspritelump - ::g->firstspritelump + 1;
	::g->spritewidth = ( fixed_t* )DoomLib::Z_Malloc( ::g->numspritelumps * 4, PU_STATIC, 0 );
	::g->spriteoffset = ( fixed_t* )DoomLib::Z_Malloc( ::g->numspritelumps * 4, PU_STATIC, 0 );
	::g->spritetopoffset = ( fixed_t* )DoomLib::Z_Malloc( ::g->numspritelumps * 4, PU_STATIC, 0 );

	for( i = 0 ; i < ::g->numspritelumps ; i++ )
	{
		if( !( i & 63 ) )
		{
			I_Printf( "." );
		}

		patch = ( patch_t* )W_CacheLumpNum( ::g->firstspritelump + i, PU_CACHE_SHARED );
		::g->spritewidth[i] = SHORT( patch->width ) << FRACBITS;
		::g->spriteoffset[i] = SHORT( patch->leftoffset ) << FRACBITS;
		::g->spritetopoffset[i] = SHORT( patch->topoffset ) << FRACBITS;
	}
}



//
// R_InitColormaps
//
void R_InitColormaps( void )
{
	int	lump, length;

	// Load in the light tables,
	//  256 byte align tables.
	lump = W_GetNumForName( "COLORMAP" );
	length = W_LumpLength( lump ) + 255;
	::g->colormaps = ( lighttable_t* )DoomLib::Z_Malloc( length, PU_STATIC, 0 );
	::g->colormaps = ( byte* )( ( ( intptr_t )::g->colormaps + 255 ) & ~0xff );
	W_ReadLump( lump, ::g->colormaps );
}



//
// R_InitData
// Locates all the lumps
//  that will be used by all views
// Must be called after W_Init.
//
void R_InitData( void )
{
	R_InitTextures();
	I_Printf( "\nInitTextures" );
	R_InitFlats();
	I_Printf( "\nInitFlats" );
	R_InitSpriteLumps();
	I_Printf( "\nInitSprites" );
	R_InitColormaps();
	I_Printf( "\nInitColormaps" );
}



//
// R_FlatNumForName
// Retrieval, get a flat number for a flat name.
//
int R_FlatNumForName( const char* name )
{
	int		i;
	char	namet[9];

	i = W_CheckNumForName( name );

	if( i == -1 )
	{
		namet[8] = 0;
		memcpy( namet, name, 8 );
		I_Error( "R_FlatNumForName: %s not found", namet );
	}
	return i - ::g->firstflat;
}




//
// R_CheckTextureNumForName
// Check whether texture is available.
// Filter out NoTexture indicator.
//
int	R_CheckTextureNumForName( const char* name )
{
	int		i;

	// "NoTexture" marker.
	if( name[0] == '-' )
	{
		return 0;
	}

	for( i = 0 ; i < ::g->s_numtextures ; i++ )
		if( !idStr::Icmpn( ::g->s_textures[i]->name, name, 8 ) )
		{
			return i;
		}

	return -1;
}



//
// R_TextureNumForName
// Calls R_CheckTextureNumForName,
//  aborts with error message.
//
int	R_TextureNumForName( const char* name )
{
	int		i;

	i = R_CheckTextureNumForName( name );

	if( i == -1 )
	{
		I_Error( "R_TextureNumForName: %s not found",
				 name );
	}
	return i;
}




//
// R_PrecacheLevel
// Preloads all relevant graphics for the level.
//

void R_PrecacheLevel( void )
{
	int			i;
	int			j;
	int			k;
	int			lump;

	texture_t*		texture;
	thinker_t*		th;
	spriteframe_t*	sf;

	if( ::g->demoplayback )
	{
		return;
	}

	// Precache flats.
	std::vector<char> flatpresent( ::g->numflats, 0 );

	for( i = 0 ; i < ::g->numsectors ; i++ )
	{
		flatpresent[::g->sectors[i].floorpic] = 1;
		flatpresent[::g->sectors[i].ceilingpic] = 1;
	}

	::g->flatmemory = 0;

	for( i = 0 ; i < ::g->numflats ; i++ )
	{
		if( flatpresent[i] )
		{
			lump = ::g->firstflat + i;
			::g->flatmemory += lumpinfo[lump].size;
			W_CacheLumpNum( lump, PU_CACHE_SHARED );
		}
	}

	// Precache textures.
	std::vector<char> texturepresent( ::g->s_numtextures, 0 );

	for( i = 0 ; i < ::g->numsides ; i++ )
	{
		texturepresent[::g->sides[i].toptexture] = 1;
		texturepresent[::g->sides[i].midtexture] = 1;
		texturepresent[::g->sides[i].bottomtexture] = 1;
	}

	// Sky texture is always present.
	// Note that F_SKY1 is the name used to
	//  indicate a sky floor/ceiling as a flat,
	//  while the sky texture is stored like
	//  a wall texture, with an episode dependend
	//  name.
	texturepresent[::g->skytexture] = 1;

	::g->texturememory = 0;
	for( i = 0 ; i < ::g->s_numtextures ; i++ )
	{
		if( !texturepresent[i] )
		{
			continue;
		}

		texture = ::g->s_textures[i];

		for( j = 0 ; j < texture->patchcount ; j++ )
		{
			lump = texture->patches[j].patch;
			::g->texturememory += lumpinfo[lump].size;
			W_CacheLumpNum( lump , PU_CACHE_SHARED );
		}
	}

	// Precache ::g->sprites.
	std::vector<char> spritepresent( ::g->numsprites, 0 );

	for( th = ::g->thinkercap.next ; th != &::g->thinkercap ; th = th->next )
	{
		if( th->function.acp1 == ( actionf_p1 )P_MobjThinker )
		{
			spritepresent[( ( mobj_t* )th )->sprite] = 1;
		}
	}

	::g->spritememory = 0;
	for( i = 0 ; i < ::g->numsprites ; i++ )
	{
		if( !spritepresent[i] )
		{
			continue;
		}

		for( j = 0 ; j < ::g->sprites[i].numframes ; j++ )
		{
			sf = &::g->sprites[i].spriteframes[j];
			for( k = 0 ; k < 8 ; k++ )
			{
				lump = ::g->firstspritelump + sf->lump[k];
				::g->spritememory += lumpinfo[lump].size;
				W_CacheLumpNum( lump , PU_CACHE_SHARED );
			}
		}
	}
}





